<?php
class My_Calendar implements Iterator, Countable
{
    /**
     * @var array
     */
    protected $days = array();
    /**
     * @var Zend_Date
     */
    protected $from;
    /**
     * @var Zend_Date
     */
    protected $to;
    /**
     * @var array
     */
    protected $events = array();
    
    /**
     * @param Zend_Date $from
     * @param Zend_Date $to 
     */
    public function __construct(Zend_Date $from, Zend_Date $to)
    {
        $this->from = $from;
        $this->to = $to;
        $this->createDaysRange();
    }
    
    /**
     * @param Zend_Date|string $from
     * @param Zend_Date|string $to
     * @return My_Calendar 
     */
    public static function factory($from = null, $to = null)
    {
        if (is_null($to)) $to = Zend_Date::now ();
        if (!($to instanceof Zend_Date)) $to = new Zend_Date($to, 'Y-MM-dd HH:mm:ss');
        
        if (is_null($from)) 
        {
            $from = Zend_Date::now();
            $from->sub(30, Zend_Date::DAY);
        }
        if (!($from instanceof Zend_Date)) $from = new Zend_Date($from, 'Y-MM-dd HH:mm:ss');
        return new self($from, $to);
    }
    
    /**
     * @return integer
     */
    public function count()
    {
        return count($this->days);
    }

    /**
     * @return mixed
     */
    public function current()
    {
        $currentDate = current($this->days);
        if (false == $currentDate) return false;
        $events = $this->getEvents($currentDate);
        return My_Calendar_Day::factory($currentDate, $events);
    }

    /**
     * @return mixed
     */
    public function key()
    {
        return key($this->days);
    }

    /**
     * @return mixed
     */
    public function next()
    {
        return next($this->days);
    }

    /**
     * @return mixed
     */
    public function rewind()
    {
        return reset($this->days);
    }

    /**
     * @return boolean
     */
    public function valid()
    {
        return !is_null(key($this->days));
    }


    /**
     * @return Zend_Date|string
     */
    public function getFrom($returnString = false)
    {
        $from = $this->from;
        if (true == $returnString) $from = $from->get('Y-MM-dd HH:mm:ss');
        return $from;
    }

    /**
     * @param Zend_Date|string $from
     * @return My_Calendar 
     */
    public function setFrom(Zend_Date $from)
    {
        if (!($from instanceof Zend_Date)) $from = new Zend_Date($from, 'Y-MM-dd HH:mm:ss');
        $this->from = $from;
        $this->createDaysRange();
        return $this;
    }

    /**
     * @return Zend_Date|string
     */
    public function getTo($returnString = false)
    {
        $to = $this->to;
        if (true == $returnString) $to = $to->get('Y-MM-dd HH:mm:ss');
        return $to;
    }

    /**
     * @param Zend_Date $to
     * @return My_Calendar 
     */
    public function setTo(Zend_Date $to)
    {
        if (!($to instanceof Zend_Date)) $to = new Zend_Date($to, 'Y-MM-dd HH:mm:ss');
        $this->to = $to;
        $this->createDaysRange();
        return $this;
    }

    /**
     * @return array
     */
    public function getEvents($date = null, $useTime = false)
    {
        $events = array();
        if (is_null($date)) 
        {
            $events = $this->events;
        }
        else
        {
            if (!($date instanceof Zend_Date)) $date = new Zend_Date($date, 'Y-MM-dd HH:mm:ss');
            if (isset ($this->events[$date->get('Y-MM-dd')]))
            {
                $events = $this->events[$date->get('Y-MM-dd')];
                if (true == $useTime)
                {
                    $timeEvents = array();
                    if (isset ($events[$date->get('Y-MM-dd')]))
                    {
                        $timeEvents = $events[$date->get(Zend_Date::TIME_SHORT)];
                    }
                    $events = $timeEvents;
                }
            }
        }
        return $events;
    }

    /**
     * @param Zend_Date|string $date Zend_Date or string ISO_8601 format
     * @param mixed $event 
     * @return My_Calendar
     */
    public function addEvent($date, $event)
    {
        if (!($date instanceof Zend_Date))
        {
            $date = new Zend_Date($date, 'Y-MM-dd HH:mm:ss');
        }
        $this->events[$date->get('Y-MM-dd')][$date->get(Zend_Date::TIME_SHORT)][] = $event; 
        return $this;
    }
    
    /**
     * @param Zend_Date|string $date Zend_Date or string ISO_8601 format
     * @param array $events 
     * @return My_Calendar
     */
    public function addEvents($date, $events)
    {
        if (!($date instanceof Zend_Date))
        {
            $date = new Zend_Date($date, 'Y-MM-dd HH:mm:ss');
        }
        foreach ($events as $event) $this->addEvent($date, $event);
        return $this;
    }
    
    protected function createDaysRange()
    {
        $range = array();
        $strDateFrom = $this->calculateFirstDay(true);
        $strDateTo = $this->calculateLastDay(true);
        $iDateFrom = mktime(1, 0, 0, substr($strDateFrom, 5, 2), substr($strDateFrom, 8, 2), substr($strDateFrom, 0, 4));
        $iDateTo = mktime(1, 0, 0, substr($strDateTo, 5, 2), substr($strDateTo, 8, 2), substr($strDateTo, 0, 4));

        if ($iDateTo >= $iDateFrom)
        {
            array_push($range, date('Y-m-d', $iDateFrom));
            while ($iDateFrom < $iDateTo)
            {
                $iDateFrom+=86400; // add 24 hours
                array_push($range, date('Y-m-d', $iDateFrom));
            }
        }
        $this->days = $range;
    }
    
    /**
     * @param boolean $returnString
     * @return Zend_Date|string
     */
    protected function calculateFirstDay($returnString = false)
    {
        $firstDay = clone $this->getFrom();
        $weekDay = $firstDay->get(Zend_Date::WEEKDAY_DIGIT);
        if ($weekDay != 1) $firstDay->sub ($weekDay-1, Zend_Date::DAY);
        if (true == $returnString) $firstDay = $firstDay->get('Y-MM-dd HH:mm:ss');
        return $firstDay;
    }
    
    /**
     * @param boolean $returnString
     * @return Zend_Date|string 
     */
    protected function calculateLastDay($returnString = false)
    {
        $lastDay = clone $this->getTo();
        $weekDay = $lastDay->get(Zend_Date::WEEKDAY_DIGIT);
        if ($weekDay != 7) $lastDay->add (7-$weekDay, Zend_Date::DAY);
        if (true == $returnString) $lastDay = $lastDay->get('Y-MM-dd HH:mm:ss');
        return $lastDay;
    }

    /**
     * @param Zend_Date|string $date
     * @param array $events 
     * @return My_Calendar_Day
     */
    public function getDay($date)
    {
        if (!($date instanceof Zend_Date))
        {
            $date = new Zend_Date($date, 'Y-MM-dd HH:mm:ss');
        }
        return My_Calendar_Day::factory($date, $this->getEvents($date));
    }
    
    
}